const express = require('express');
const bodyParser = require('body-parser');
const { AbortController } = require('abort-controller');

const app = express();
app.use(bodyParser.json());

// 并发控制配置
const MAX_CONCURRENT = 8;
const taskMap = new Map(); // 存储任务映射：normalizedScript => Set<AbortController>

class Semaphore {
  constructor(max) {
    this.max = max;
    this.count = 0;
    this.queue = [];
  }

  acquire() {
    return new Promise(resolve => {
      if (this.count < this.max) {
        this.count++;
        resolve();
      } else {
        this.queue.push(resolve);
      }
    });
  }

  release() {
    if (this.queue.length > 0) {
      const next = this.queue.shift();
      next();
    } else {
      this.count--;
    }
  }
}

const semaphore = new Semaphore(MAX_CONCURRENT);

// 标准化脚本内容（删除多余空格）
function normalizeScript(script) {
  return script
    .replace(/\s+/g, ' ')
    .replace(/\s*([;{}()])\s*/g, '$1')
    .trim();
}

// 原业务逻辑函数
function splitStatements(input) {
    let statements = [];
    let current = '';
    let level = 0;
    for (let c of input.replace(/\n/g, ' ')) {
        if (c === '{') level++;
        else if (c === '}') level--;
        if (c === ';' && level === 0) {
            statements.push(current.trim());
            current = '';
        } else {
            current += c;
        }
    }
    if (current.trim()) statements.push(current.trim());
    return statements;
}

async function executeScript(input, signal) {
    const statements = splitStatements(input);
    for (const stmt of statements) {
        if (signal.aborted) throw new Error('Execution aborted');
        await executeStatement(stmt, signal);
    }
}

async function executeStatement(stmt, signal) {
    if (!stmt) return;
    if (stmt.startsWith('set')) {
        await handleSet(stmt, signal);
    } else if (stmt.startsWith('while')) {
        await handleWhileLoop(stmt, signal);
    } else if (stmt.startsWith('if')) {
        await handleIfStatement(stmt, signal);
    } else {
        throw new Error(`Unknown statement: ${stmt}`);
    }
}

async function handleSet(stmt, signal) {
    const match = stmt.match(/^set\s+(?:\[([^\]]+)\]|(\S+))\s+(.+)$/);
    if (!match) throw new Error(`Invalid set statement: ${stmt}`);
    const key = (match[1] || match[2]).trim();
    const value = parseValue(match[3].trim());
    await setValueByUnifiedKey(key, value, "int", signal);
}

function parseValue(valueStr) {
    const num = Number(valueStr);
    return isNaN(num) ? valueStr : num;
}

async function handleWhileLoop(stmt, signal) {
    const match = stmt.match(/^while\s*\(([^)]*)\)\s*{([^}]*)}/);
    if (!match) throw new Error(`Invalid while loop: ${stmt}`);
    const conditionExpr = match[1].trim();
    const body = match[2].trim();

    while (true) {
        if (signal.aborted) throw new Error('Execution aborted');
        const conditionResult = await evaluateCondition(conditionExpr, signal);
        if (!conditionResult) break;
        await executeScript(body, signal);
        await new Promise(resolve => setTimeout(resolve, 1000));
    }
}

async function handleIfStatement(stmt, signal) {
    const match = stmt.match(/^if\s*\(([^)]*)\)\s*{([^}]*)}/);
    if (!match) throw new Error(`Invalid if statement: ${stmt}`);
    const conditionExpr = match[1].trim();
    const body = match[2].trim();

    const conditionResult = await evaluateCondition(conditionExpr, signal);
    if (conditionResult) {
        await executeScript(body, signal);
    }
}

async function evaluateCondition(expr, signal) {
    const match = expr.match(/^get\s+(?:\[([^\]]+)\]|(\S+))\s*(<|>|=|<=|>=|==| )\s*(-?\d+\.?\d*)$/i);
    if (!match) throw new Error(`Invalid evaluateCondition expr: ${expr}`);

    const key = (match[1] || match[2]).trim();
    const cmp = match[3].trim();
    const rightValue = parseValue(match[4]);

    const leftValue = await getValueByUnifiedKey(key, signal);
    const numLeft = Number(leftValue);
    const numRight = Number(rightValue);

    if (isNaN(numLeft) || isNaN(numRight)) {
        throw new Error(`Invalid number comparison: ${leftValue} ${cmp} ${rightValue}`);
    }

    switch (cmp) {
        case '<': return numLeft < numRight;
        case '>': return numLeft > numRight;
        case '<=': return numLeft <= numRight;
        case '>=': return numLeft >= numRight;
        case '': case '=': case '==': return numLeft === numRight;
        default: throw new Error(`Unsupported comparator: ${cmp}`);
    }
}

class HttpService {
    constructor(baseURL) {
        this.baseURL = baseURL;
    }

    async request(method, endpoint, data = null, signal) {
        const url = `${this.baseURL}${endpoint}`;
        const config = {
            method: method.toUpperCase(),
            headers: { 'Content-Type': 'application/json' },
            signal
        };

        if (data) config.body = JSON.stringify(data);
        console.log("endpoint: " + endpoint)
        console.log("config.body: " + config.body)
        try {
            const response = await fetch(url, config);
            console.log("response.ok: " + response.ok)

            if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
            return response;
        } catch (error) {
            if (error.name !== 'AbortError') console.error(`Request failed: ${error.message}`);
            throw error;
        }
    }

    async get(endpoint, params, signal) {
        const query = new URLSearchParams(params).toString();
        return this.request('GET', `${endpoint}?${query}`, null, signal);
    }

    async post(endpoint, data, signal) {
        return this.request('POST', endpoint, data, signal);
    }

    async post_params(endpoint, params, signal) {
        const query = new URLSearchParams(params).toString();
        return this.request('POST', `${endpoint}?${query}`, signal);
      }
}

const model_backend_url = 'http://10.149.158.72:8081';
const opcua_backend_url = 'http://10.149.158.72:8080';
const http = new HttpService(model_backend_url);
const http_opcua = new HttpService(opcua_backend_url);

async function setValueByUnifiedKey(unifiedKey, value, type, signal) {
    try {
        const result = await http.get(`/v1/profiles/${unifiedKey}`, {}, signal);
        const resultObj = await result.json();

        if (!resultObj.characteristicNodeId) {
            const res = await http.post(`/v1/profiles/${unifiedKey}/value/${value}`, {}, signal);
            await res.json();
            return value;
        } else {
            const params = { node_id: resultObj.characteristicNodeId, value, type };
            const res = await http_opcua.post_params('/set', params, signal);
            return res.json();
        }
    } catch (error) {
        if (error.name !== 'AbortError') console.error(`Set failed: ${unifiedKey}`, error);
        throw error;
    }
}

async function getValueByUnifiedKey(unifiedKey, signal) {
    try {
        const result = await http.get(`/v1/profiles/${unifiedKey}`, {}, signal);
        const resultObj = await result.json();

        if (!resultObj.characteristicNodeId) {
            return resultObj.value;
        } else {
            const res = await http_opcua.get('/get', { node_id: resultObj.characteristicNodeId }, signal);
            const data = await res.json();
            return data.value;
        }
    } catch (error) {
        if (error.name !== 'AbortError') console.error(`Get failed: ${unifiedKey}`, error);
        throw error;
    }
}

// 服务器路由
app.post('/control', async (req, res) => {
    const { script: rawScript, action } = req.body;
  
    if (action === 'off') {
      if (!rawScript) {
        return res.status(400).send('Missing script parameter');
      }
  
      const normalizedScript = normalizeScript(rawScript);
      const controllers = taskMap.get(normalizedScript);
  
      if (!controllers || controllers.size === 0) {
        return res.status(404).send('No matching tasks found');
      }
  
      // 中止所有匹配的任务
      controllers.forEach(controller => {
        controller.abort();
        controllers.delete(controller);
      });
      taskMap.delete(normalizedScript);
  
      return res.send(`Terminated ${controllers.size} task(s)`);
    }
  
    if (action === 'on') {
      if (!rawScript) {
        return res.status(400).send('Missing script parameter');
      }
  
      const normalizedScript = normalizeScript(rawScript);
      
      try {
        await semaphore.acquire();
        const controller = new AbortController();
  
        // 维护任务映射
        if (!taskMap.has(normalizedScript)) {
          taskMap.set(normalizedScript, new Set());
        }
        taskMap.get(normalizedScript).add(controller);
  
        executeScript(rawScript, controller.signal)
          .then(() => res.send('Script executed successfully'))
          .catch(error => {
            if (error.message !== 'Execution aborted') {
              console.error('Execution error:', error);
              res.status(500).send(error.message);
            }
          })
          .finally(() => {
            semaphore.release();
            // 清理任务映射
            const controllers = taskMap.get(normalizedScript);
            if (controllers) {
              controllers.delete(controller);
              if (controllers.size === 0) {
                taskMap.delete(normalizedScript);
              }
            }
          });
  
      } catch (error) {
        if (error.name === 'AbortError') {
          res.send('Execution aborted');
        } else {
          res.status(503).send('Server busy');
        }
      }
    } else {
      res.status(400).send('Invalid action');
    }
  });


// 启动服务器
const PORT = 8082;
app.listen(PORT, '0.0.0.0', () => {
    console.log(`Server running at http://10.149.158.72:${PORT}`);
});